package data.handle;

import java.util.LinkedList;

/*
* lineItem文件的关键数据处理类
*
* 采用 “Hash表 + 小根堆” 结构对该文件的关键数据进行保存和处理
*
* Hash表 ：根据零件号的取模运算来计算出它在hash值，hash表的长度设置为当前线程所处理的零件号的数量，
*          hash表中存储Node节点，其key值是零件号，value值是零件数量、该零件的销售总额、该节点所在小根堆在堆链表中的序号、该节点在小根堆中的数组下标。
*
* 小根堆 ：在小根堆中存储的Node节点与hash表中Node节点是同一个对象，但是小根堆中以每种零件的零件数量为key值建立结构，
*         并且在小根堆建立的过程中，key值即零件销量是动态变化的，需要实时的维护小根堆结构。
*
* 在本处理类中，会以品牌号为分类建立25个小根堆链表，每一个小根堆链表负责维护一个品牌号，
* 在该链表中可能会存在多个小根堆节点，每个小根堆节点负责维护2500个Node节点
* （设计目的是考虑到在不同零件号分片即在不同处理线程中，每个品牌所获得的零件种类数量是不确定的）
*
* 整个处理流程为：首先会根据零件号与关联的partHandle对象（零件号分片相同）得到该零件号的品牌号，之后计算出其在hash表中的位置，
* 再根据位置获得hash表中该零件号的Node节点，在Node节点中得到该节点所在小根堆在堆链表中的序号、在小根堆中的数组下标，
* 得到这两个数据后就可以定位到节点的位置，最后根据put的数据插入新的Node节点或者修改Node中的数据并且对小根堆进行实时的维护。
*
* */
public class LineItemHandle {
    //在一个数据处理结构中处理的零件号的数量
    private int partKeyNum;
    //记录当前零件号分片上零件的销售总量
    private int quantitySum;
    //零件号分片相同的part文件处理类
    private PartHandle partHandle;
    //Hash表结构
    private Node[] lineItemNodes;
    //小根堆链表数组
    private HeapLinkedList[] heaps;

    public LineItemHandle(int partKeyNum, PartHandle partHandle){
        this.partKeyNum = partKeyNum;
        this.partHandle = partHandle;
        lineItemNodes = new Node[partKeyNum];
        //建立25个品牌号的小根堆链表数组
        heaps = new HeapLinkedList[25];

        for (int i = 0; i < partKeyNum; i++) {
            lineItemNodes[i] = new Node();
        }

        for (int i = 0; i < 25; i++) {
            heaps[i] = new HeapLinkedList();
        }
    }

    /*
     * @Description: 向lineItemHandle对象中存放关键数据
     * @param partKey : 零件号
     * @param quantity : 零件数量（销量）
     * @param price : 在该行订单项中的零件价格
     * @return: {@link void}
     * @author: xff
     * @Date: 2022/7/5
     */
    public void put(int partKey, int quantity, double price){
        this.quantitySum += quantity;
        //计算hash值
        int hash = partKey % partKeyNum;
        Node lineItemNode = lineItemNodes[hash];
        //获取品牌号
        byte brandIndex = partHandle.getBrandIndex(partKey);
        //将由MN（M：1~5，N：1~5）组成的零件号转换成对应的数组下标
        int index = indexConversion(brandIndex);
        HeapLinkedList heap = heaps[index];
        //零件号为0表示该零件号是第一次处理
        if (lineItemNode.getPartKey() == 0) {
            lineItemNode.setPartKey(partKey);
            lineItemNode.setQuantity(quantity);
            lineItemNode.setPriceSum(price);
            heap.insertHeap(lineItemNode);//向小根堆中放入Node节点
        } else {
            //非第一次处理
            lineItemNode.setQuantity(lineItemNode.getQuantity() + quantity);
            lineItemNode.setPriceSum(lineItemNode.getPriceSum() + price);
            //维护小根堆结构
            heap.heapify(lineItemNode);
        }
    }

    /*
     * @Description: 查询指定品牌中小于quantity数量的结果
     * @param brandIndex : 零件品牌
     * @param quantity : 指定的一个销量
     * @return: {@link java.util.LinkedList<data.handle.LineItemHandle.Node>}
     * @author: xff
     * @Date: 2022/7/5
     */
    public LinkedList<Node> select(byte brandIndex, double quantity){
        //将由MN（M：1~5，N：1~5）组成的零件号转换成对应的数组下标
        int index = indexConversion(brandIndex);
        HeapLinkedList heap = heaps[index];
        return heap.popAll(quantity);
    }

    //将由MN（M：1~5，N：1~5）组成的零件号转换成对应的数组下标
    public int indexConversion(byte brandIndex){
        int a = 0;
        if(brandIndex <= 15 && brandIndex >= 11){
            a = brandIndex - 11;
        }
        if(brandIndex <= 25 && brandIndex >= 21){
            a = brandIndex - 21 + 5;
        }
        if(brandIndex <= 35 && brandIndex >= 31){
            a = brandIndex - 31 + 10;
        }
        if(brandIndex <= 45 && brandIndex >= 41){
            a = brandIndex - 41 + 15;
        }
        if(brandIndex <= 55 && brandIndex >= 51){
            a = brandIndex - 51 + 20;
        }
        return a;
    }

    public int getQuantitySum(){
        return quantitySum;
    }


    //小根堆链表
    public class HeapLinkedList{
        private HeapLinkedListNode head;
        private HeapLinkedListNode tail;
        private byte heapIndex;

        //取出小根堆链表中所有小根堆节点中小于指定零件数量的Node节点
        public LinkedList<Node> popAll(double quantity){
            HeapLinkedListNode cur = head;
            LinkedList<Node> list = new LinkedList<>();
            for (int i = 0; i <= heapIndex; i++) {
                Node node = cur.value.pop();
                while(node.getQuantity() < quantity){
                    list.add(node);
                    node = cur.value.pop();
                }
                cur = cur.next;
            }
            return list;
        }

        //向小根堆链表中出入Node节点
        public void insertHeap(Node node){
            if (head == null){
                head = new HeapLinkedListNode();
                tail = head;
            }

            //链表尾节点小根堆中Node的数量小于2500会向其中插入新节点，否则会创建一个新的小根堆节点
            if (tail.value.size < 2500) {
                node.setHeapIndex(heapIndex);
                tail.value.insertHeapify(node);
            } else {
                heapIndex++;
                HeapLinkedListNode newNode = new HeapLinkedListNode();
                tail.next = newNode;
                tail = newNode;
                node.setHeapIndex(heapIndex);
                tail.value.insertHeapify(node);
            }
        }

        //对修改了零件数量（堆的key值）的Node节点进行小根堆结构的维护
        public void heapify(Node node){
            //得到小根堆在链表中的序号
            byte heapIndex = node.getHeapIndex();
            HeapLinkedListNode cur = head;
            for (int i = 0; i < heapIndex; i++) {
                cur = cur.next;
            }
            //定位到在堆数组中的位置，维护小根堆结构
            cur.value.heapify(node.getPositionInHeap());
        }
    }

    //堆链表的节点类
    static class HeapLinkedListNode{
        private NodeHeap value;//堆结构对象
        private HeapLinkedListNode next;

        public HeapLinkedListNode(){
            value = new NodeHeap(2500);//每个堆结构的大小是2500个节点组织起来
            next = null;
        }

    }

    //堆结构类
    static class NodeHeap{
        private Node[] heapNodes;//小根堆数组
        private int size;//小根堆中有效的Node节点数量

        public NodeHeap(int length){
            heapNodes = new Node[length];
        }

        //取出堆顶节点
        public Node pop(){
            Node node = heapNodes[0];
            swap(0, size - 1);
            size--;
            heapify(0);
            return node;
        }

        //向小根堆中插入新节点
        private void insertHeapify(Node node){
            heapNodes[size] = node;
            int index = size++;
            while (heapNodes[index].getQuantity() < heapNodes[(index - 1)/2].getQuantity()){
                swap(index, (index - 1)/2);
                index = (index - 1)/2;
            }
        }

        //维护小根堆结构，index:在堆中的下标
        private void heapify(int index){
            int heapSize = size;
            int left = index * 2 + 1;
            int right = index * 2 + 2;
            while(left < heapSize){
                int small = right < heapSize && heapNodes[left].getQuantity() > heapNodes[right].getQuantity()? right : left;
                if(heapNodes[small].getQuantity() < heapNodes[index].getQuantity()){
                    swap(small, index);
                    left = small * 2 + 1;
                    right = left + 1;
                    index = small;
                }else
                    break;
            }
        }

        //节点位置交换
        private void swap(int index1, int index2){
            Node a = heapNodes[index1];
            heapNodes[index1] = heapNodes[index2];
            heapNodes[index2] = a;
            //修改Node节点中在小根堆中下标的值
            heapNodes[index1].setPositionInHeap(index1);
            heapNodes[index2].setPositionInHeap(index2);
        }

    }

    //记录数据的Node节点
    static class Node{
        private int quantity;
        private int partKey;
        private double priceSum;

        private byte heapIndex;
        private int positionInHeap;

        public Node(){
        }
        public Node(int partKey){
            this.partKey = partKey;
        }

        public int getQuantity() {
            return quantity;
        }

        public void setQuantity(int quantity) {
            this.quantity = quantity;
        }

        public int getPartKey() {
            return partKey;
        }

        public void setPartKey(int partKey) {
            this.partKey = partKey;
        }

        public double getPriceSum() {
            return priceSum;
        }

        public void setPriceSum(double priceSum) {
            this.priceSum = priceSum;
        }

        public byte getHeapIndex() {
            return heapIndex;
        }

        public void setHeapIndex(byte heapIndex) {
            this.heapIndex = heapIndex;
        }

        public int getPositionInHeap() {
            return positionInHeap;
        }

        public void setPositionInHeap(int positionInHeap) {
            this.positionInHeap = positionInHeap;
        }
    }

}
